#pragma once

#include <iostream>
#include<string>
#include<vector>
#include<unordered_map>
#include<fstream>
#include<mutex>
#include"Isparse.hpp"


namespace ns_index{
    
    struct DocInfo{
        std::string title;//文档标题
        std::string content;//文档内容
        std::string url;//文档URL
        u_int64_t doc_id;//文档ID
    };

    struct InvertedElem{
        u_int64_t doc_id;//文档ID
        std::string world; //关键字
        int weight;//权重
    };

    typedef std::vector<InvertedElem> invertedeList;
    class index{
        private:
        //正排索引的数据结构用数组，数组的下标天然是文档的ID（forward_index）
        std::vector<DocInfo> forward_index;

        //倒排索引一定是一个关键字和一组（个）InvertedElem对应（用unordered_mao<string,倒排的结构体数组>）
        std::unordered_map<std::string,invertedeList> Inverted_index;

        private:
        index(){}//构造函数私有
        index(const index&)=delete;//禁止拷贝构造函数
        index& operator=(const index&)=delete;//禁止拷贝赋值函数

        static index* instance;//单例模式
        static std::mutex mutex;//互斥锁
        public:
        ~index(){}//析构函数
//单例模式
        public:
        static index* GetInstance()//获取单例
        {
            mutex.lock();
            if(nullptr==instance)
            {
                instance=new index();
            }
            mutex.unlock();

            return instance;
        }
        //根据doc_id找到文档内容
        DocInfo *GetForwardlndex(u_int64_t doc_id)
        {
            if(doc_id>forward_index.size())
            {
                std::cout<<"GetForwardlndex error"<<std::endl;
                return nullptr;
            }

            return &forward_index[doc_id];
        }
        //根据关键字String，获取倒排拉链
        invertedeList *GetInvertedList(const std::string &world)
        {
            auto de=Inverted_index.find(world);
            if(de==nullptr)
            {
                std::cout<<"GetInvertedList error "<<std::endl;
                return nullptr;
            }

            return &de->second;
        }
        //根据去标签，格式化之后的文档，构建正排和倒排索引
        bool BuildIndex(const std::string &input)//将pares处理完毕的数据交给我
        {
            std::ifstream in(input,std::ios::in|std::ios::binary);
            if(!in.is_open())
            {
                std::cout<<input<<" is open error "<<std::endl;
                return false;
            }
            std::string line;//每行数据
            int n=0;
            while(std::getline(in,line))//逐行读取
            {
                DocInfo * doc=BuildForwardIndex(line);//构建正排索引
                if(doc==nullptr)
                {
                    std::cout<<"BuildForwardIndex error "<<line<<std::endl;
                    continue;
                }
                
                if(!BuildInvertedIndex(*doc))//构建倒排索引
                {
                    std::cout<<"BuildInvertedIndex error "<<doc<<std::endl;
                    continue;
                }
            }
            return true;
        }
        //构建正排索引
        private:
        DocInfo *BuildForwardIndex(const std::string &line)
        {
            //1 简析line，字符串切分
	        //line->title content url;
            std::vector<std::string> results;
            //切分字符串
            std::string sep="\3";
            ParseModel::StringUtil::CutString(line,&results,sep);
            if(results.size()!=3)
            {
                std::cout<<"切分失败 "<<std::endl;
                return nullptr;
            }
            // 2. 字符串进行填充到Docinfo中
            DocInfo doc;
            doc.title=results[0];
            doc.content=results[1];
            doc.url=results[2];
            doc.doc_id=results.size();//先进行保存id,在插入时,对应的id就是当前doc在vect中的下标
            // 3. 插入到正排索引的vector
            forward_index.push_back(std::move(doc));


            return &forward_index.back();
        }
        //构建倒排索引
        bool BuildInvertedIndex(const DocInfo &doc)
        {
            struct word_cnt{
                int title_cnt;
                int content_cnt;

                word_cnt():title_cnt(0),content_cnt(0){}
            };
            //对title的切分
            std::unordered_map<std::string,word_cnt> word_map;//词频统计
            std::vector<std::string> title_words;//切分后的title词
            ParseModel::JiebaUtil::CutString(doc.title,&title_words);//切分title

            for(auto &e:title_words) 
            {
                word_map[e].title_cnt++;//如果找到就直接++，没有找到就新建后再++
            }

            //对content的切分
            std::vector<std::string> content_words;
            ParseModel::JiebaUtil::CutString(doc.title,&content_words);

            for(auto &e:content_words)
            {
                word_map[e].content_cnt++;//如果找到就直接++，没有找到就新建后再++
            }

            #define X 10
            #define Y 1
            //填充InvertedElem
            for(auto &word_pair:word_map)
            {
                InvertedElem item;
                item.doc_id=doc.doc_id;
                item.world=word_pair.first;
                item.weight=X*word_pair.second.title_cnt+Y*word_pair.second.content_cnt;

                //建立倒排
                invertedeList &invertede_list=Inverted_index[word_pair.first];//获取对应的倒排列表
                invertede_list.push_back(std::move(item));//插入到倒排列表中
            }
            return true;
        }
    };

    index* index::instance=nullptr;//单例模式,初始化为nullptr,在构造函数中赋值,在析构函数中释放内存
    std::mutex index::mutex;//互斥锁
}